package info.motteke.util;

import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;
import java.util.Random;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class CryptPicker extends Picker {

    private final Charset charset = Charset.forName("UTF-8");

    private final byte[] keyBytes = createKeyBytes("hogehoge", 128);

    public CryptPicker(Properties prop) {
        super(prop);
    }

    @Override
    public String get(String key) {
        String value = prop.getProperty(key);

        if(value == null){
            return null;
        } else {
            return decrypt(value);
        }
    }

    @Override
    public String get(String key, String defaultValue) {
        String value = get(key);
        return value == null ? null : value;
    }

    @Override
    public void set(String key, String value) {
        prop.setProperty(key, encrypt(value));
    }

    private byte[] createKeyBytes(String string, int bits) {
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
            byte[] seed = md5.digest(string.getBytes(charset));
            long l = 0;
            for(byte b : seed){
                l = (l + b + 128) * 31;
            }
            Random r = new Random(l);
            byte[] ret = new byte[bits / 8];
            r.nextBytes(ret);

            return ret;
        } catch (NoSuchAlgorithmException e) {
            throw new AssertionError(e);
        }
    }

    private String encrypt(String value) {
        return Base64.encode(exec(Cipher.ENCRYPT_MODE, value.getBytes(charset)));
    }

    private String decrypt(String value) {
        return new String(exec(Cipher.DECRYPT_MODE, Base64.decode(value)), charset);
    }

    private Key getKey(){
        return new SecretKeySpec(keyBytes, "AES");
    }

    private byte[] exec(int operation, byte[] value){
        Throwable cause;
        try {
            Cipher c = Cipher.getInstance("AES");
            c.init(operation, getKey());
            return c.doFinal(value);
        } catch (InvalidKeyException e) {
            cause = e;
        } catch (NoSuchAlgorithmException e) {
            cause = e;
        } catch (NoSuchPaddingException e) {
            cause = e;
        } catch (IllegalBlockSizeException e) {
            cause = e;
        } catch (BadPaddingException e) {
            cause = e;
        }
        throw new IllegalArgumentException(cause);
    }
}
